IReadOnly
IReadOnlyCollection & IReadOnlyList β Immutable Access
"Use IReadOnly* interfaces to expose collections without allowing modification."
β Bad example:
public class Portfolio
{
private List<Position> _positions = new();
public List<Position> Positions => _positions; // exposes internal list
}
// Caller can mutate internal state
var portfolio = new Portfolio();
portfolio.Positions.Add(new Position()); // breaks encapsulation
portfolio.Positions.Clear(); // disaster!
Exposing mutable collections lets callers break invariants and bypass validation.
β Good example:
public class Portfolio
{
private List<Position> _positions = new();
public IReadOnlyCollection<Position> Positions => _positions;
public void AddPosition(Position position)
{
ValidatePosition(position);
_positions.Add(position);
}
}
// Caller can only read
var portfolio = new Portfolio();
int count = portfolio.Positions.Count; // β
allowed
// portfolio.Positions.Add(...); // β compiler error
π IReadOnlyCollection
π₯ Using IReadOnlyList for indexed access:
public class TradingDay
{
private List<Trade> _trades = new();
public IReadOnlyList<Trade> Trades => _trades;
public void RecordTrade(Trade trade)
{
_trades.Add(trade);
}
}
// Caller can access by index, but not modify
var day = new TradingDay();
var firstTrade = day.Trades[0]; // β
indexed access
int count = day.Trades.Count; // β
count
// day.Trades.Add(...); // β compiler error
π IReadOnlyList
π₯ Avoiding defensive copies:
// β Bad: creates unnecessary copy
public IEnumerable<Order> GetOrders()
{
return _orders.ToList(); // allocates new list every call
}
// β
Good: exposes read-only view without copying
public IReadOnlyCollection<Order> GetOrders()
{
return _orders; // no allocation, just interface cast
}
π List
π‘ In trading systems:
- Expose position snapshots as IReadOnlyCollection
to prevent accidental modifications. - Return IReadOnlyList
for price history where indexed access is useful. - Prevent invariant violations by hiding Add/Remove while keeping data accessible.
---
Questions & Answers
Q: What's the difference between IReadOnlyCollection
A: IReadOnlyList
*Q: Does IReadOnly guarantee immutability?**
A: No. It prevents modification through the interface, but underlying data can still change. If the backing List
Q: Can I cast List
A: Yes. List
Q: What's ReadOnlyCollection
A: ReadOnlyCollection
Q: Should I return IReadOnlyList
A: If Count and indexed access are useful to callers and data is already materialized (not lazy), yes. IReadOnlyList
Q: How do I create a truly immutable collection?
A: Use ImmutableList<T> from System.Collections.Immutable. Modifications return new instances. Or wrap with new ReadOnlyCollection<T>(list).
Q: Can I expose arrays as IReadOnlyList
A: Yes, arrays implement IReadOnlyListArray.AsReadOnly(array) for true protection.
Q: What's the performance of IReadOnlyList
A: Identical. IReadOnlyList
Q: How do I mock IReadOnlyCollection
A: Use arrays or List
Q: When should I use IReadOnlyCollection
A: When Count is useful to callers and data is already materialized. IEnumerable